package com.meta.nd.dao.impl;

import com.meta.cache.NdCacheService;
import com.meta.common.CommonInfo;
import com.meta.common.ConstDef;
import com.meta.nd.dao.NvFilterDAO;
import com.meta.nd.filter.bo.FilterParamDef;
import com.meta.nd.filter.vo.FilterFd;
import com.meta.nd.service.filter.utils.SqlBuilder;
import com.meta.nd.util.NodeUtil;
import com.meta.nd.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigInteger;
import java.util.*;

@Slf4j
@Transactional
@Component
public class NvFilterDAOImpl implements NvFilterDAO {

	@Autowired
	private SessionFactory sessionFactory;
	
	@Autowired	
	NdCacheService ndCacheService;



private void setSqlParamValue(Nd nd, Query q, FilterParamDef filterDef) {
	for (FilterFd filterFd : filterDef.getFilterFds()) {
		if (filterFd.isExpression()) continue;
		String fdName = filterFd.getFdName();
		String dataType = null;
		if (fdName.equals(ConstDef.FD_NAME_ID) || fdName.equals(ConstDef.FD_NAME_PARENT_ID))
			dataType = ConstDef.DATA_TYPE_LONG;
		else if (fdName.equals(ConstDef.FD_NAME_META_ND_CODE))
			dataType = ConstDef.DATA_TYPE_STRING;
		else {
			Nd currentNd = nd.getDescendantByName(filterFd.getNdName());
			dataType = currentNd.getFdByName(fdName).getDataType();
		}
		if ( ConstDef.LOGIC_OPERATION_LEFT_LIKE.equals(filterFd.getLogicalOperator())
			||ConstDef.LOGIC_OPERATION_RIGHT_LIKE.equals(filterFd.getLogicalOperator())
			||ConstDef.LOGIC_OPERATION_DOUBLE_LIKE.equals(filterFd.getLogicalOperator())
		) {
			String value = filterFd.getValue().toString();
			switch (filterFd.getLogicalOperator()) {
				case ConstDef.LOGIC_OPERATION_LEFT_LIKE:
					if (!value.endsWith("%"))
						value += "%";
					break;
				case ConstDef.LOGIC_OPERATION_RIGHT_LIKE:
					if (!value.startsWith("%"))
						value = "%" + value;
					break;
				case ConstDef.LOGIC_OPERATION_DOUBLE_LIKE:
					if (!value.endsWith("%")) value += "%";
					if (!value.startsWith("%"))
						value = "%" + value;
					break;
				default:
					break;
			}
			q.setString(fdName, value);
		}
		else if (dataType.equals(ConstDef.DATA_TYPE_DATE))
			q.setDate(fdName, (java.util.Date) filterFd.getValue());
		else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME))
			q.setTimestamp(fdName, (java.util.Date) filterFd.getValue());
		else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
			q.setDouble(fdName, (Double) filterFd.getValue());
		} else if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
			q.setLong(fdName, (Long) filterFd.getValue());
		} else if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
			q.setInteger(fdName, (Integer) filterFd.getValue());
		} else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
			q.setString(fdName, (String) filterFd.getValue());
		} else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
			q.setBoolean(fdName, (Boolean) filterFd.getValue());
		}
	}
}


private int filterNvsCount(FilterParamDef filterDef) {
	Nd nd = ndCacheService.getNd(filterDef.getTargetNdCode());
	String sql = SqlBuilder.buildTotalSql(nd,filterDef);
	Session session = this.sessionFactory.getCurrentSession();
	Query q = session.createSQLQuery(sql);
	this.setSqlParamValue(nd, q,filterDef);
	try {
		List<BigInteger> list = q.list();
		int count = list.get(0).intValue();
		return count;
	}catch (Exception e ){
		throw new RuntimeException("执行filterNvsCount错误！",e);
	}
}

	@Override
	public 	List<Map<String,Object>> filterRecordMap(CommonInfo commonInfo, String sql, Map<String,Object> parmMap){
		Session session = this.sessionFactory.getCurrentSession();
		Query q = session.createSQLQuery(sql);
		this.setSqlParamValue(q,parmMap);
		List<Map<String, Object>> resultList = (List<Map<String, Object>>) q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
		return resultList;
	}

	private void setSqlParamValue(Query q, Map<String,Object> parmMap) {
		Object value;
		for (String fdName : parmMap.keySet()) {
			value = parmMap.get(fdName);
			String dataType = null;
			if ( value instanceof Long)
				dataType = ConstDef.DATA_TYPE_LONG;
			else if ( value instanceof Integer)
				dataType = ConstDef.DATA_TYPE_INT;
			else if ( value instanceof String)
				dataType = ConstDef.DATA_TYPE_STRING;
			else if ( value instanceof Date)
				dataType = ConstDef.DATA_TYPE_DATE;
			else if ( value instanceof  Float)
				dataType = ConstDef.DATA_TYPE_FLOAT;
			else if ( value instanceof Double)
				dataType = ConstDef.DATA_TYPE_FLOAT;
			else if ( value instanceof  Boolean)
				dataType= ConstDef.DATA_TYPE_BOOLEAN;
			else if ( value instanceof List)
				dataType = ConstDef.DATA_TYPE_STRING_LIST;

			if (dataType.equals(ConstDef.DATA_TYPE_DATE))
				q.setDate(fdName, (java.util.Date) value);
			else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME))
				q.setTimestamp(fdName, (java.util.Date) value);
			else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
				q.setDouble(fdName, (Double) value);
			} else if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
				q.setLong(fdName, (Long) value);
			} else if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
				q.setInteger(fdName, (Integer) value);
			} else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
				q.setString(fdName, (String) value);
			} else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
				q.setBoolean(fdName, (Boolean) value);
			}
			else q.setParameterList(fdName,((List)value).toArray());

		}
	}
	

@Override
public ListQueryResult<Nv> filterNvs(FilterParamDef filterDef) {
	int totalCount = filterNvsCount(filterDef);
	ListQueryResult<Nv> queryResult = new ListQueryResult();
	queryResult.setTotalCount(totalCount);
	List<Map<String, Object>> resultList = filterRecordMap(filterDef);
	Nd resultNd = ndCacheService.getNd(filterDef.getResultNdCode());
	for (Map<String, Object> record : resultList) {
		Nv nv = filterRecord2Nv(resultNd,record);
		queryResult.getResult().add(nv);
	}
	return queryResult;
}

@Override
public List<Map<String,Object>> filterRecordMap(FilterParamDef filterDef) {
	int start = filterDef.getStartNo();
	int count = filterDef.getCount();
	Nd nd = this.ndCacheService.getNd(filterDef.getTargetNdCode());
	String sql = SqlBuilder.build(nd, filterDef);
	Session session = this.sessionFactory.getCurrentSession();
	Query q = session.createSQLQuery(sql);
	this.setSqlParamValue(nd,q,filterDef);
	q.setFirstResult(start);
	q.setMaxResults(count);
	List<Map<String, Object>> resultList = (List<Map<String, Object>>) q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
	return resultList;
}

private Nv filterRecord2Nv( Nd rootNd, Map<String, Object> record) {
	String code = (String) record.get(ConstDef.FD_NAME_META_ND_CODE);
	Nv nv = NodeUtil.nd2Nv(rootNd);
	long id = 0;
	if ( record.get(ConstDef.FD_NAME_ID) != null)
		id = ((BigInteger)record.get(ConstDef.FD_NAME_ID)).longValue();
	long parentId = 0L;
	if (record.get(ConstDef.FD_NAME_PARENT_ID) != null)
		parentId =  ((BigInteger)record.get(ConstDef.FD_NAME_PARENT_ID)).longValue();
	nv.setId(id);
	nv.setParentId(parentId);
	nv.setMetaNdCode(rootNd.getNdCode());
	nv.setActualNdCode(code);

	for ( String name : record.keySet() ) {
		if ( nv.getFvByName(name) == null)
			continue;

		Object value = record.get(name);
		Fd fd = rootNd.getFdByName(name);

		if ( fd.getDataType().equals(ConstDef.DATA_TYPE_LONG)) {
			long longValue = 0;
			if (value != null)
				longValue = ((BigInteger) value ).longValue();
			nv.setFvByName(name, longValue);
		}
		else
			nv.setFvByName(name, value);
	}
	return nv;
}




	


}
